IHG.SelectionController.prototype = {
	_getSelection: function()
	{
		if(this.cache.selection === undefined) {
			this.cache.isMarked  = [],
			this.cache.isWrapped = [],
			this.cache.selection = {
				pre: this.txtarea.value.substr(0, this.start()).toLowerCase(),
				text:this.selection().toLowerCase(),
				post:this.txtarea.value.substr(this.end()).toLowerCase()
			}
		}
	},
	
	selection: function() // "OK"
	{
		return this.txtarea.value.substring(this.start(), this.end());
	},

	start: function() // OK
	{
		this.viewportPosition = this.txtarea.scrollTop; // voor scroll-positie
		
		return this.txtarea.selectionStart;
	},

	end: function() // OK
	{
		return this.txtarea.selectionEnd;
	},
	
	isMarked: function(openTag, closeTag, returnPosition)
	{
		var identifier = openTag + '-' + closeTag;
				
		this._getSelection();
		
		if(this.cache.isMarked[identifier] ===  undefined) {
			var openTagPosition  = this.cache.selection.pre.lastOccurrenceOf(openTag).index;
			var closeTagPosition = this.cache.selection.pre.lastOccurrenceOf(closeTag).index;
			//console.log('isMarked', openTag, closeTag, this.cache.selection.pre.lastOccurrenceOf(openTag));
			this.cache.isMarked[identifier] = openTagPosition > closeTagPosition ? openTagPosition : null;
		}
		
		if(returnPosition)
			return this.cache.isMarked[identifier];
		else
			return this.cache.isMarked[identifier] !== null;
	},
	
	isWrapped: function(openTag, closeTag)
	{
		var identifier = openTag + '-' + closeTag;
		
		this._getSelection();
		
		if(this.cache.isWrapped[identifier] === undefined) {
			this.cache.isWrapped[identifier] = 
				this.cache.selection.post.lastOccurrenceOf(closeTag).index !== -1 &&
				this.cache.selection.pre.lastOccurrenceOf(closeTag).index < this.cache.selection.pre.lastOccurrenceOf(openTag).index &&
				(this.cache.selection.post.occurrenceOf(closeTag).index < this.cache.selection.post.occurrenceOf(openTag).index || 
				 this.cache.selection.post.occurrenceOf(openTag).index == -1)
		}
			
		return this.cache.isWrapped[identifier];
	},
	
	selectionLength: function()
	{
		this._getSelection();
		
		return this.cache.selection.text.length;
	},

	move: function(start, end) // "OK"
	{
		/**
		 * Bugs:
		 * - Opera maakt van een selectie waarbij start = end lengte 3, terwijl lengte 0 verwacht is
		 */
			
		//console.log("Move carrot to ", start, end);
					
		if(typeof end != "number") end = start;
	
		this.txtarea.select();
	
		this.txtarea.selectionStart = start;
		this.txtarea.selectionEnd = end;
	
		this.txtarea.scrollTop = this.viewportPosition;
		//console.log("from Move", this.start(), this.end());
	},

	mark: function(lft, rgt) // OK
	{
		/**
		 * [        A        [sele -B- ction]         C        ]
		 * 0                 1              2                  3
		 */
	
		/* 1 */ var selStart = this.start();
		/* 2 */ var selEnd = this.end();
		
		var isCollapsed  = selStart == selEnd;
			
		/* A */ var txtLftOfCarrot = this.txtarea.value.substring(0, selStart);
		/* B */ var txtUnderCarrot = isCollapsed ? '' : this.txtarea.value.substring(selStart, selEnd);
		/* C */ var txtRgtOfCarrot = this.txtarea.value.substring(selEnd);
	
		/**
		 * STRING												isCollapsed		selStart		selEnd	Action
		 * blaat [b]schaap^ [b]paas[/b] haas				true				15				15			place closing tag
		 * blaat [b]schaap^[/b] paas haas					true				15				15			unwrap
		 * [b]blaat[/b] ^schaap [b]paas[/b] haas			true				13				13			place opening tag
		 * [b]blaat[/b] ^schaap^ [b]paas[/b] haas			false				13				20			wrap
		 */
		if(isCollapsed) {
			/* laatste tag is gesloten: plaats open-tag */
			if(!this.isMarked(lft, rgt)) {
				this.txtarea.value = txtLftOfCarrot + lft + txtUnderCarrot + txtRgtOfCarrot;
				this.move(selStart + lft.length, selEnd + lft.length);
			}
			/* laatste tag is open, voeg sluit-tag toe */
			else {
				var openTags = [];
				var _self = this;
				each(this.tags, function(tag) {
					if(!tag[0] || !tag[1]) return;
					var position = _self.isMarked(tag[0], tag[1], true);
					if(position !== null) {
						openTags.push({
							position: position,
							tag: tag
						});
					}
				});
				
				openTags.sort(function (a, b) {
					return (a.position - b.position) * -1;
				});
				
			 	closingTags = '';
				each(openTags, function(item) {
					closingTags += item.tag[1];
					if(item.tag[1] == rgt) 
						throw IHG.$break;
				})
				
				this.txtarea.value = txtLftOfCarrot + closingTags + txtUnderCarrot + txtRgtOfCarrot;
				this.move(selStart + closingTags.length, selEnd + closingTags.length);
			}
		}
		/* selectie: markeer de selectie met open- en sluit-tag */
		else {
			this.wrap(lft, rgt);
		}
	},
	
	wrap: function(lft, rgt)
	{
		var selStart = this.start();
		var selEnd = this.end();
		
		var isCollapsed  = selStart == selEnd;
		
		var txtLftOfCarrot = this.txtarea.value.substring(0, selStart);
		var txtUnderCarrot = isCollapsed ? '' : this.txtarea.value.substring(selStart, selEnd);
		var txtRgtOfCarrot = this.txtarea.value.substring(selEnd);
		
		this.txtarea.value = txtLftOfCarrot + lft + txtUnderCarrot + rgt + txtRgtOfCarrot;
		
		this.move(
			selStart + lft.length,
			selEnd   + lft.length
		);
	},

	unwrap: function(lft, rgt) // @todo: regular expression gebruiken.
	{
		/**
		 * [   A   [x]   B   [sele -C- ction]   D   [/x]   E   ]
		 * 0       1 2       3              4       5  6       7
		 */
	
		/* 3 */ var selStart = this.start();
		/* 4 */ var selEnd = this.end();
			
		var _lastLftOccurrence = this.txtarea.value.toLowerCase().substring(0, selStart).lastOccurrenceOf(lft);
		/* 1 */ var lastLftOccurrence = _lastLftOccurrence.index;
				var lftLength = _lastLftOccurrence.length;
		var _firstRgtOccurrence = this.txtarea.value.toLowerCase().substr(selEnd).occurrenceOf(rgt);
		/* 5 */ var firstRgtOccurrence = _firstRgtOccurrence.index + selEnd;
				var rgtLength = _firstRgtOccurrence.length;
	
		var txtLeftOfCarrot = 
		/* A */ this.txtarea.value.substring(0, lastLftOccurrence) +
		/* B */ this.txtarea.value.substring(lastLftOccurrence + lftLength, selStart);
		
		var txtUnderCarrot = 
		/* C */ this.txtarea.value.substring(selStart, selEnd);
		
		var txtRightOfCarrot = 
		/* D */ this.txtarea.value.substring(selEnd, firstRgtOccurrence) +
		/* E */ this.txtarea.value.substring(firstRgtOccurrence + rgtLength);
	
		this.txtarea.value = 
		/* AB */ txtLeftOfCarrot + 
		/* C  */ txtUnderCarrot + 
		/* DE */ txtRightOfCarrot;
	
		this.move(
		/* 3 */ selStart - lftLength,
		/* 4 */ selEnd - lftLength
		);
	},

	replace: function(replacement)
	{
		var selStart = this.start();
		var selEnd = this.end();
			
		var txtLftOfCarrot = this.txtarea.value.substring(0, selStart);
		var txtUnderCarrot = replacesment;
		var txtRgtOfCarrot = this.txtarea.value.substring(selEnd);
	
		this.txtarea.value = 
			txtLftOfCarrot + lft + txtUnderCarrot + rgt + txtRgtOfCarrot;
			
		this.move(
			selStart,
			selStart   + replacement.length
		);
	}
}